/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.saber.certificacao.beansjsf.prova.controler;

import br.com.saber.certificacao.beansjsf.login.LoginManager;
import br.com.saber.certificacao.beansjsf.semescopo.SelectItemManager;
import br.com.saber.certificacao.ejbs.stateless.interfaces.ProvaBeanLocal;
import br.com.saber.certificacao.enums.TipoProva;
import br.com.saber.certificacao.enums.TipoQuestao;
import br.com.saber.certificacao.modelos.Prova;
import br.com.saber.certificacao.modelos.Resposta;
import br.com.saber.certificacao.modelos.relacionamentos.ProvaQuestao;
import br.com.saber.certificacao.modelos.validadores.exceptions.generica.ValidacaoExcecao;
import br.com.saber.certificacao.observador.ControleObserver;
import br.com.saber.certificacao.utils.UtilMetodos;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import org.apache.commons.lang.StringUtils;

/**
 *
 * @author gbvbahia
 */
public abstract class ProvaService {

    //Bean de manutenção de provas
    protected ProvaBeanLocal provaBean;

    //Prova a responder
    protected Prova prova;

    //Utilizado para determinar a questão atual.
    protected ProvaQuestao questaoAtual;

    //Todas as questões da prova dentro de um ProvaQuestao
    protected List<ProvaQuestao> provaQuestoes;

    //Utilizado para disable do botão proxima
    protected boolean existeQuestaoProxima;

    //Utilizado para disable do botão anterior
    protected boolean existeQuestaoAnterior;

    //Utilizado para tipo de resposta selectOne ou SelectMany
    protected boolean unicaEscolha;

    public ProvaService(Prova prova, ProvaBeanLocal provaBean) {
        this.prova = prova;
        this.provaBean = provaBean;
        this.provaQuestoes = new ArrayList<ProvaQuestao>(new TreeSet<ProvaQuestao>(prova.getQuestoesDestaProva()));
        this.questaoAtual = this.provaQuestoes.get(0);
        this.existeQuestaoProxima = true;
        this.existeQuestaoAnterior = false;
        this.unicaEscolha = questaoAtual.getQuestao().getTipoQuestao().equals(TipoQuestao.UNICA_ESCOLHA) ? true : false;
    }

    //====================
    //Métodos de Abstratos
    //====================
    /**
     * Define se libera observações das questões respondidas
     * Depende do tipo de prova.
     * @return true para liberar false para não liberar
     */
    public abstract void liberarObservacao(FacesContext fc);

    /**
     * Determina se é para exibir a observação da questão ou não.
     * É para ser um get da varial booleana atualiza em 
     * liberarObservacao(FacesContext fc).
     * Para economia de recursos não deve se fazer essa avaliação
     * dentro do get, somente quando solicitado pelo usuário.
     * @return true para exibir e false para não exibir.
     */
    public abstract boolean isLiberaObservacao(FacesContext facesContext);

    /**
     * Define se os comentarios podem ser exibidos ou não
     * @return 
     */
    public abstract boolean isProvaComentada(FacesContext facesContext);

    /**
     * Retorna Lista com comentarios das respostas exibidas na prova
     * Atenção que a ordem deve ser a mesma.
     * Para caso em que as observações não deverão ser exibidas
     * retorna uma lista vazia.
     * @return List String das respostas.
     */
    public abstract List<String> getRespostasDisponiveisObservacao(FacesContext facesContext);

    /**
     * Exibe uma informação na tela sobre o carregamento da prova.
     * @param facesContext 
     */
    public abstract void exibeInformacaoSobreTipoDeProva(FacesContext facesContext);
    
    
    public abstract TipoProva tipoProva(FacesContext facesContext); 
    
    //====================
    //Métodos de Negócio
    //====================
    /**
     * Termina a prova e salva no banco de dados.
     */
    public void terminarProva(FacesContext fc, LoginManager loginManager) {
        try {
            this.prova.setProvaTerminada(true);
            atualizaProva();
            ControleObserver.notificaObservers(loginManager.getUsuario(), new String[]{ControleObserver.Eventos.PROVA_TERMINO});
            UtilMetodos.messageFactoringFull("provaComentadaFazendoTerminada", FacesMessage.SEVERITY_INFO, fc);
        } catch (ValidacaoExcecao e) {
            if (!StringUtils.isBlank(e.getAtributoName())) {
                UtilMetodos.messageFactoringFull(UtilMetodos.getResourceBundle(e.getMessage(), fc), null, e.getAtributoName(), FacesMessage.SEVERITY_ERROR, fc);
            } else {
                UtilMetodos.messageFactoringFull(e.getMessage(), FacesMessage.SEVERITY_ERROR, fc);
            }
        }
    }

    /**
     * Passa para proxima questão
     */
    public void questaoProxima() {
        this.questaoAtual = this.provaQuestoes.get(this.questaoAtual.getNumeroQuestao());
        atualizaProva();
        liberaProximaAnterior();
    }

    /**
     * Retorna para a questão anterior
     */
    public void questaoAnterior() {
        this.questaoAtual = this.provaQuestoes.get(this.questaoAtual.getNumeroQuestao() - 2);
        atualizaProva();
        liberaProximaAnterior();
    }

    /**
     * Salva a prova no banco de dados
     */
    public void atualizaProva() {
        prova.setQuestoesDestaProva(new TreeSet<ProvaQuestao>(this.provaQuestoes));
        this.provaBean.atualizarProva(prova);
    }

    /**
     * Velibera os botões próxima questão e questão anterior
     */
    private void liberaProximaAnterior() {
        if (this.questaoAtual.getNumeroQuestao() == this.provaQuestoes.size()) {
            existeQuestaoProxima = false;
        } else {
            existeQuestaoProxima = true;
        }
        if (this.questaoAtual.getNumeroQuestao() == 1) {
            existeQuestaoAnterior = false;
        } else {
            existeQuestaoAnterior = true;
        }
        this.unicaEscolha = questaoAtual.getQuestao().getTipoQuestao().equals(TipoQuestao.UNICA_ESCOLHA) ? true : false;
    }

    //====================
    //SelectItem
    //====================
    /**
     * O TreeSet é somente para garantir a ordem de exibição
     * @return 
     */
    public List<SelectItem> getRespostasDisponiveis(SelectItemManager selectItemManager) {
        return selectItemManager.getRespostasDisponiveis(new TreeSet<Resposta>(this.questaoAtual.getQuestao().getRespostas()));
    }

    //=========================
    //Getters AND Setters
    //=========================
    /**
     * Somente as questões da prosição 1
     * @return Listas de questões do lado esquerdo
     */
    public List<ProvaQuestao> getProvaQuestoesUm() {
        Set<ProvaQuestao> um = new TreeSet<ProvaQuestao>();
        int i = 1;
        if (this.provaQuestoes != null) {
            for (ProvaQuestao pq : this.provaQuestoes) {
                if (pq.getNumeroQuestao() == i) {
                    um.add(pq);
                    i += 3;
                }
            }
        }
        return new ArrayList<ProvaQuestao>(um);
    }

    /**
     * Somente as questões da prosição 2
     * @return Listas de questões do centro
     */
    public List<ProvaQuestao> getProvaQuestoesDois() {
        Set<ProvaQuestao> dois = new TreeSet<ProvaQuestao>();
        int i = 2;
        if (this.provaQuestoes != null) {
            for (ProvaQuestao pq : this.provaQuestoes) {
                if (pq.getNumeroQuestao() == i) {
                    dois.add(pq);
                    i += 3;
                }
            }
        }
        return new ArrayList<ProvaQuestao>(dois);
    }

    /**
     * Somente as questões da prosição 3
     * @return Listas de questões do lado direito
     */
    public List<ProvaQuestao> getProvaQuestoesTres() {
        Set<ProvaQuestao> tres = new TreeSet<ProvaQuestao>();
        if (this.provaQuestoes != null) {
            for (ProvaQuestao pq : this.provaQuestoes) {
                if (pq.getNumeroQuestao() % 3 == 0) {
                    tres.add(pq);
                }
            }
        }
        return new ArrayList<ProvaQuestao>(tres);
    }

    /**
     * Aqui existe somente uma resposta, já que é para tipo UNICA_ESCOLHA
     * @return A resposta macada, se houver, null se não houver
     */
    public Resposta getRespostaMarcada() {
        if (!(this.questaoAtual.getRespostasMarcadasNaProva().isEmpty())) {
            return (Resposta) this.questaoAtual.getRespostasMarcadasNaProva().toArray()[0];
        } else {
            return null;
        }
    }

    /**
     * Aqui existe somente uma resposta, já que é para tipo UNICA_ESCOLHA
     * @param respostaMarcada 
     */
    public void setRespostaMarcada(Resposta respostaMarcada) {
        this.questaoAtual.addRespostaMarcada(respostaMarcada);
    }

    /**
     * Retorna uma list com todas as respostas marcadas,
     * para tipo MULTIPLAS_RESPOSTAS
     * @return 
     */
    public List<Resposta> getRespostasMarcadas() {
        return new ArrayList<Resposta>(this.questaoAtual.getRespostasMarcadasNaProva());
    }

    /**
     * Retorna uma list com todas as respostas marcadas,
     * para tipo MULTIPLAS_RESPOSTAS
     * @param respostasMarcadas 
     */
    public void setRespostasMarcadas(List<Resposta> respostasMarcadas) {
        this.questaoAtual.setRespostasMarcadasNaProva(new TreeSet<Resposta>(respostasMarcadas));
    }

    public Prova getProva() {
        return prova;
    }

    public boolean isExisteQuestaoAnterior() {
        return existeQuestaoAnterior;
    }

    public boolean isExisteQuestaoProxima() {
        return existeQuestaoProxima;
    }

    public boolean isUnicaEscolha() {
        return unicaEscolha;
    }

    public ProvaQuestao getQuestaoAtual() {
        return questaoAtual;
    }

    public List<ProvaQuestao> getProvaQuestoes() {
        return provaQuestoes;
    }
}
